package edu.asu.sapa.sas;

import java.util.*;

import org.jgrapht.*;
import org.jgrapht.graph.*;

public class SAS {
	/** Undefined or "any value" */
	public static final int UNDEFINED = -1;

	int numVars;

	protected ArrayList<Variable> varList;

	protected ArrayList<Condition> goalList;

	protected HashMap<Variable, ArrayList<Integer>> goalValues;

	protected HashMap<Variable, Integer> initialValues;

	protected ArrayList<SASOperator> operatorList;

	protected ArrayList<Effect> effectList;

	protected ArrayList<Prevail> prevailList;

	// a list of the atoms in this domain
	protected ArrayList<Atom> atomList;

	protected ArrayList<ArrayList<ValueVertex>> vertexValues = new ArrayList<ArrayList<ValueVertex>>();

	/**
	 * Creates a new SAS object. Though a domain transition graph can be created
	 * directly after calling this constructor, an atom list may be needed for
	 * some purposes (such as correlating the utility of goals with STRIPS
	 * atoms). To set this call the method setAtoms(ArrayList<Atom>).
	 * 
	 * @param varList
	 * @param stateMap
	 * @param goalMap
	 * @param opList
	 * @param effList
	 * @param prevailList
	 */
	public SAS(ArrayList<Variable> varList,
			HashMap<Variable, Integer> stateMap,
			HashMap<Variable, ArrayList<Integer>> goalMap,
			ArrayList<SASOperator> opList, ArrayList<Effect> effList,
			ArrayList<Prevail> prevailList) {
		this.varList = varList;
		this.prevailList = prevailList;
		numVars = varList.size();
		operatorList = opList;
		initialValues = stateMap;
		goalValues = goalMap;
		this.effectList = effList;
	}

	/**
	 * Set the atoms for this domain.
	 * 
	 * @param atomList
	 *            Set the list of atoms for this domain.
	 */
	public void setAtoms(ArrayList<Atom> atomList) {
		this.atomList = atomList;
	}

	public HashMap<Variable, ArrayList<Integer>> getGoals() {
		return goalValues;
	}
	
	public boolean isGoal(Variable var, int val) {
		ArrayList<Integer> varGoals = goalValues.get(var);
		if (varGoals != null && varGoals.contains(val)) {
			return true;
		}
		return false;
	}

	public ArrayList<Variable> getVariables() {
		return varList;
	}

	public ArrayList<SASOperator> getOperators() {
		return operatorList;
	}

	public ArrayList<Effect> getEffects() {
		return effectList;
	}

	public ArrayList<Prevail> getPrevails() {
		return prevailList;
	}

	public ArrayList<ValueVertex> getVertices(int i) {
		return vertexValues.get(i);
	}

	public ValueVertex getVertex(int i, int j) {
		return vertexValues.get(i).get(j);
	}

	public int getInitValue(Variable var) {
		return initialValues.get(var);
	}

	public void setNewInitValue(Variable var, int value) {
		initialValues.put(var, value);
	}
	
	public ArrayList<Atom> getAtoms() {
		return atomList;
	}

	/**
	 * Gives the domain transition graph for each variable.
	 */
	public ArrayList<DirectedGraph<ValueVertex, EffectArc>> createDTG() {
		ArrayList<DirectedGraph<ValueVertex, EffectArc>> graphs = 
			new ArrayList<DirectedGraph<ValueVertex, EffectArc>>();

		for (Variable var : varList) {
			graphs.add(createVariableGraph(var));
		}

		return graphs;
	}

	protected DirectedGraph<ValueVertex, EffectArc> createVariableGraph(
			Variable var) {
		ArrayList<ValueVertex> vertices = createVertices(var);
		DirectedGraph<ValueVertex, EffectArc> varGraph = 
			new DirectedMultigraph<ValueVertex, EffectArc>(EffectArc.class);

		// add vertices to the graph
		for (ValueVertex v : vertices) {
			varGraph.addVertex(v);
		}

		ValueVertex s;
		ValueVertex t;
		EffectArc edge;
		// add an edge for each effect
		for (Effect eff : var.getEffects()) {
			edge = new EffectArc();
			edge.setEffect(eff);
			edge.setID(eff.getID());
			// get vertex for "old" value
			s = vertices.get(eff.oldValue);
			t = vertices.get(eff.newValue);
			varGraph.addEdge(s, t, edge);
		}

		// setup prevails in vertices
		SASOperator op;
		int value;
		for (Prevail prevail : var.getPrevails()) {
			op = prevail.getOperator();
			value = prevail.getValueID();
			vertices.get(value).addPrevailsOperator(op);
		}

		return varGraph;
	}

	protected ArrayList<ValueVertex> createVertices(Variable var) {
		ArrayList<ValueVertex> vertices = new ArrayList<ValueVertex>();
		int numV = var.getDomain();
		for (int i = 0; i < numV; i++) {
			vertices.add(new ValueVertex(var, i));
		}

		vertexValues.add(var.getID(), vertices);
		return vertices;
	}
}
